/*
 Xern Entity Relocation Network
 Copyright (C) 2010-2011 Ronie Salgado <roniesalg@gmail.com>

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
*/


// Add some support code. 
%{
namespace delegate_helper {

	// Type descriptor getter
	template <typename T>
	struct TypeDescriptor {};
	
	template <typename T>
	struct TypeDescriptor<T*>
	{
		static swig_type_info *Get()
		{
			return TypeDescriptor<T>::Get();
		}
	};
	
	// MyGUI::Widget
	template <>
	struct TypeDescriptor<MyGUI::Widget>
	{
		static swig_type_info *Get()
		{
			return SWIGTYPE_p_MyGUI__Widget;
		}
	};

	template <>
	struct TypeDescriptor<MyGUI::Window>
	{
		static swig_type_info *Get()
		{
			return SWIGTYPE_p_MyGUI__Window;
		}
	};
	
	template <>
	struct TypeDescriptor<MyGUI::ToolTipInfo>
	{
		static swig_type_info *Get()
		{
			return SWIGTYPE_p_MyGUI__ToolTipInfo;
		}
	};
	
	template <>
	struct TypeDescriptor<MyGUI::KeyCode>
	{
		static swig_type_info *Get()
		{
			return SWIGTYPE_p_MyGUI__KeyCode;
		}
	};
	
	template <>
	struct TypeDescriptor<MyGUI::MouseButton>
	{
		static swig_type_info *Get()
		{
			return SWIGTYPE_p_MyGUI__MouseButton;
		}
	};
	
	// Argument pusher function
	template <typename T>
	struct ArgPusher
	{
		static void Push(HSQUIRRELVM v, T obj)
		{
			ArgPusher<const T&>::Push (v, obj);
		}
	};

	template <typename T>
	struct ArgPusher<T*>
	{
		static void Push(HSQUIRRELVM v, T *pointer)
		{
			SWIG_NewPointerObj(v,pointer,TypeDescriptor<T>::Get(),0);
		}
	};
		
	template <typename T>
	struct ArgPusher<T&>
	{
		static void Push(HSQUIRRELVM v, T &obj)
		{
			ArgPusher<T*>::Push (v, &obj);
		}
	};
	
	template <typename T>
	struct ArgPusher<const T&>
	{
		static void Push(HSQUIRRELVM v, const T &obj)
		{
			T *copy = new T(obj);
			SWIG_NewPointerObj(v,copy,TypeDescriptor<T>::Get(),1);
		}
	};
	
	template<>
	struct ArgPusher<bool>
	{
		static void Push(HSQUIRRELVM v, bool value)
		{
			sq_pushbool(v, value);
		}
	};
	
	template<>
	struct ArgPusher<int>
	{
		static void Push(HSQUIRRELVM v, int value)
		{
			sq_pushinteger(v, value);
		}
	};
	
	template<>
	struct ArgPusher<unsigned int>
	{
		static void Push(HSQUIRRELVM v, unsigned int value)
		{
			sq_pushinteger(v, value);
		}
	};

	template<>
	struct ArgPusher<float>
	{
		static void Push(HSQUIRRELVM v, float value)
		{
			sq_pushfloat(v, value);
		}
	};
	
	template<>
	struct ArgPusher<const size_t &>
	{
		static void Push(HSQUIRRELVM v, const size_t &value)
		{
			sq_pushinteger(v, value);
		}
	};

	template<>
	struct ArgPusher<size_t*>
	{
		static void Push(HSQUIRRELVM v, size_t *value)
		{
			sq_pushinteger(v, *value);
		}
	};
	
	template<>
	struct ArgPusher<const std::string&>
	{
		static void Push(HSQUIRRELVM v, const std::string &str)
		{
			sq_pushstring(v, str.data(), str.size());
		}
	};
	
	template<>
	struct ArgPusher<std::string*>
	{
		static void Push(HSQUIRRELVM v, std::string *str)
		{
			sq_pushstring(v, str->data(), str->size());
		}
	};

				
	// Squirrel delegate.
	using namespace MyGUI::delegates;
	class SquirrelDelegate0: public IDelegate0
	{
	public:
		SquirrelDelegate0(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
			: vm(vm), env(env), func(func)
		{
			sq_addref(vm, &env);
			sq_addref(vm, &func);
			assert(sq_isclosure(func) || sq_isnativeclosure(func));
		}
		
		~SquirrelDelegate0()
		{
			sq_release(vm, &env);
			sq_release(vm, &func);
		}
	
		virtual bool isType( const std::type_info& _type)
		{
			return typeid(SquirrelDelegate0) == _type;
		}
		
		virtual void invoke()
		{
			// Push the function.
			sq_pushobject(vm, func);
			
			// Push the environment.
			sq_pushobject(vm, env);
			
			// Call the function.
			sq_call(vm, 1, SQFalse, SQFalse); 
		}
		
		virtual bool compare(IDelegate0 * _delegate) const
		{
			if (nullptr == _delegate || !_delegate->isType(typeid(SquirrelDelegate0)) )
			 return false;
			SquirrelDelegate0 * cast = static_cast<SquirrelDelegate0 *>(_delegate);
			return cast == this;
		}
		
		virtual bool compare(IDelegateUnlink * _unlink) const
		{ 
			return false;
		}
		
	private:
		HSQUIRRELVM vm;
		HSQOBJECT env;
		HSQOBJECT func;
	};
	
	template<typename TP1>
	class SquirrelDelegate1: public IDelegate1<TP1>
	{
	public:
		SquirrelDelegate1(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
			: vm(vm), env(env), func(func)
		{
			sq_addref(vm, &env);
			sq_addref(vm, &func);
			assert(sq_isclosure(func) || sq_isnativeclosure(func));
		}
		
		~SquirrelDelegate1()
		{
			sq_release(vm, &env);
			sq_release(vm, &func);
		}
	
		virtual bool isType( const std::type_info& _type)
		{
			return typeid(SquirrelDelegate1<TP1>) == _type;
		}
		
		virtual void invoke(TP1 arg1)
		{
			// Push the function.
			sq_pushobject(vm, func);
			
			// Push the environment.
			sq_pushobject(vm, env);
			
			// Push the arguments
			ArgPusher<TP1>::Push(vm, arg1);
			
			// Call the function.
			sq_call(vm, 2, SQFalse, SQFalse); 
		}
		
		virtual bool compare(IDelegate1<TP1> * _delegate) const
		{
			if (nullptr == _delegate || !_delegate->isType(typeid(SquirrelDelegate1<TP1>)) )
			 return false;
			SquirrelDelegate1<TP1> * cast = static_cast<SquirrelDelegate1<TP1> *>(_delegate);
			return cast == this;
		}
		
		virtual bool compare(IDelegateUnlink * _unlink) const
		{ 
			return false;
		}
		
	private:
		HSQUIRRELVM vm;
		HSQOBJECT env;
		HSQOBJECT func;
	};
	
	template<typename TP1, typename TP2>
	class SquirrelDelegate2: public IDelegate2<TP1, TP2>
	{
	public:
		SquirrelDelegate2(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
			: vm(vm), env(env), func(func)
		{
			sq_addref(vm, &env);
			sq_addref(vm, &func);
			assert(sq_isclosure(func) || sq_isnativeclosure(func));
		}
		
		~SquirrelDelegate2()
		{
			sq_release(vm, &env);
			sq_release(vm, &func);
		}
	
		virtual bool isType( const std::type_info& _type)
		{
			return typeid(SquirrelDelegate2<TP1, TP2>) == _type;
		}
		
		virtual void invoke(TP1 arg1, TP2 arg2)
		{
			// Push the function.
			sq_pushobject(vm, func);
			
			// Push the environment.
			sq_pushobject(vm, env);
			
			// Push the arguments
			ArgPusher<TP1>::Push(vm, arg1);
			ArgPusher<TP2>::Push(vm, arg2);
			
			// Call the function.
			sq_call(vm, 3, SQFalse, SQFalse); 
		}
		
		virtual bool compare(IDelegate2<TP1, TP2> * _delegate) const
		{
			if (nullptr == _delegate || !_delegate->isType(typeid(SquirrelDelegate2<TP1, TP2>)) )
			 return false;
			SquirrelDelegate2<TP1, TP2> * cast = static_cast<SquirrelDelegate2<TP1, TP2> *>(_delegate);
			return cast == this;
		}
		
		virtual bool compare(IDelegateUnlink * _unlink) const
		{ 
			return false;
		}
		
	private:
		HSQUIRRELVM vm;
		HSQOBJECT env;
		HSQOBJECT func;
	};
	
	template<typename TP1, typename TP2, typename TP3>
	class SquirrelDelegate3: public IDelegate3<TP1, TP2, TP3>
	{
	public:
		SquirrelDelegate3(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
			: vm(vm), env(env), func(func)
		{
			sq_addref(vm, &env);
			sq_addref(vm, &func);
			assert(sq_isclosure(func) || sq_isnativeclosure(func));
		}
		
		~SquirrelDelegate3()
		{
			sq_release(vm, &env);
			sq_release(vm, &func);
		}
	
		virtual bool isType( const std::type_info& _type)
		{
			return typeid(SquirrelDelegate3<TP1, TP2, TP3>) == _type;
		}
		
		virtual void invoke(TP1 arg1, TP2 arg2, TP3 arg3)
		{
			// Push the function.
			sq_pushobject(vm, func);
			
			// Push the environment.
			sq_pushobject(vm, env);
			
			// Push the arguments
			ArgPusher<TP1>::Push(vm, arg1);
			ArgPusher<TP2>::Push(vm, arg2);
			ArgPusher<TP3>::Push(vm, arg3);
			
			// Call the function.
			sq_call(vm, 4, SQFalse, SQFalse); 
		}
		
		virtual bool compare(IDelegate3<TP1, TP2, TP3> * _delegate) const
		{
			if (nullptr == _delegate || !_delegate->isType(typeid(SquirrelDelegate3<TP1, TP2, TP3>)) )
			 return false;
			SquirrelDelegate3<TP1, TP2, TP3> * cast = static_cast<SquirrelDelegate3<TP1, TP2, TP3> *>(_delegate);
			return cast == this;
		}
		
		virtual bool compare(IDelegateUnlink * _unlink) const
		{ 
			return false;
		}
		
	private:
		HSQUIRRELVM vm;
		HSQOBJECT env;
		HSQOBJECT func;
	};
	
	template<typename TP1, typename TP2, typename TP3, typename TP4>
	class SquirrelDelegate4: public IDelegate4<TP1, TP2, TP3, TP4>
	{
	public:
		SquirrelDelegate4(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
			: vm(vm), env(env), func(func)
		{
			sq_addref(vm, &env);
			sq_addref(vm, &func);
			assert(sq_isclosure(func) || sq_isnativeclosure(func));
		}
		
		~SquirrelDelegate4()
		{
			sq_release(vm, &env);
			sq_release(vm, &func);
		}
	
		virtual bool isType( const std::type_info& _type)
		{
			return typeid(SquirrelDelegate4<TP1, TP2, TP3, TP4>) == _type;
		}
		
		virtual void invoke(TP1 arg1, TP2 arg2, TP3 arg3, TP4 arg4)
		{
			// Push the function.
			sq_pushobject(vm, func);
			
			// Push the environment.
			sq_pushobject(vm, env);
			
			// Push the arguments
			ArgPusher<TP1>::Push(vm, arg1);
			ArgPusher<TP2>::Push(vm, arg2);
			ArgPusher<TP3>::Push(vm, arg3);
			ArgPusher<TP4>::Push(vm, arg4);
			
			// Call the function.
			sq_call(vm, 4, SQFalse, SQFalse); 
		}
		
		virtual bool compare(IDelegate4<TP1, TP2, TP3, TP4> * _delegate) const
		{
			if (nullptr == _delegate || !_delegate->isType(typeid(SquirrelDelegate4<TP1, TP2, TP3, TP4>)) )
			 return false;
			SquirrelDelegate4<TP1, TP2, TP3, TP4> * cast = static_cast<SquirrelDelegate4<TP1, TP2, TP3, TP4> *>(_delegate);
			return cast == this;
		}
		
		virtual bool compare(IDelegateUnlink * _unlink) const
		{ 
			return false;
		}
		
	private:
		HSQUIRRELVM vm;
		HSQOBJECT env;
		HSQOBJECT func;
	};
	
	// Delegate creator
	template<typename DT>
	struct DelegateCreator {};
	
	template<>
	struct DelegateCreator<MyGUI::delegates::CDelegate0>
	{
		static SquirrelDelegate0 *Create(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
		{
			return new SquirrelDelegate0(vm, env, func);
		}
	};

	template<typename TP1>
	struct DelegateCreator<MyGUI::delegates::CDelegate1<TP1> >
	{
		static SquirrelDelegate1<TP1> *Create(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
		{
			return new SquirrelDelegate1<TP1> (vm, env, func);
		}
	};
	
	template<typename TP1, typename TP2>
	struct DelegateCreator<MyGUI::delegates::CDelegate2<TP1, TP2> >
	{
		static SquirrelDelegate2<TP1, TP2> *Create(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
		{
			return new SquirrelDelegate2<TP1, TP2> (vm, env, func);
		}
	};

	template<typename TP1, typename TP2, typename TP3>
	struct DelegateCreator<MyGUI::delegates::CDelegate3<TP1, TP2, TP3> >
	{
		static SquirrelDelegate3<TP1, TP2, TP3> *Create(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
		{
			return new SquirrelDelegate3<TP1, TP2, TP3> (vm, env, func);
		}
	};
	
	template<typename TP1, typename TP2, typename TP3, typename TP4>
	struct DelegateCreator<MyGUI::delegates::CDelegate4<TP1, TP2, TP3, TP4> >
	{
		static SquirrelDelegate4<TP1, TP2, TP3, TP4> *Create(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
		{
			return new SquirrelDelegate4<TP1, TP2, TP3, TP4> (vm, env, func);
		}
	};
	
	// Create delegate function.
	template<typename DelegateType>
	typename DelegateType::IDelegate *CreateDelegate(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func)
	{
		return DelegateCreator<DelegateType>::Create(vm, env, func);
	}
};

%}

// Include the delegate base class.
#pragma SWIG nowarn=302
%include "MyGUI_Delegate.h" // Delegate is special.

// Extend the different delegates.
%extend MyGUI::delegates::CDelegate0 {
	void set(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func) {
		*$self = new delegate_helper::SquirrelDelegate0 (vm, env, func); 
	}
	
	void set(HSQUIRRELVM vm, HSQOBJECT func) {
		HSQOBJECT env;
		
		sq_pushroottable(vm);
		sq_resetobject(&env);
		sq_getstackobj(vm, -1, &env);
		*$self = new delegate_helper::SquirrelDelegate0 (vm, env, func);
		sq_pop(vm, 1); /* pop the root table*/
	}
}

%extend MyGUI::delegates::CDelegate1 {
	void set(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func) {
		*$self = new delegate_helper::SquirrelDelegate1<TP1> (vm, env, func); 
	}
	
	void set(HSQUIRRELVM vm, HSQOBJECT func) {
		HSQOBJECT env;
		
		sq_pushroottable(vm);
		sq_resetobject(&env);
		sq_getstackobj(vm, -1, &env);
		*$self = new delegate_helper::SquirrelDelegate1<TP1> (vm, env, func);
		sq_pop(vm, 1); /* pop the root table*/
	}
}

%extend MyGUI::delegates::CDelegate2 {
	void set(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func) {
		*$self = new delegate_helper::SquirrelDelegate2<TP1, TP2> (vm, env, func); 
	}
	
	void set(HSQUIRRELVM vm, HSQOBJECT func) {
		HSQOBJECT env;
		
		sq_pushroottable(vm);
		sq_resetobject(&env);
		sq_getstackobj(vm, -1, &env);
		*$self = new delegate_helper::SquirrelDelegate2<TP1, TP2> (vm, env, func);
		sq_pop(vm, 1); /* pop the root table*/
	}
}

%extend MyGUI::delegates::CDelegate3 {
	void set(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func) {
		*$self = new delegate_helper::SquirrelDelegate3<TP1, TP2, TP3> (vm, env, func); 
	}
	
	void set(HSQUIRRELVM vm, HSQOBJECT func) {
		HSQOBJECT env;
		
		sq_pushroottable(vm);
		sq_resetobject(&env);
		sq_getstackobj(vm, -1, &env);
		*$self = new delegate_helper::SquirrelDelegate3<TP1, TP2, TP3> (vm, env, func);
		sq_pop(vm, 1); /* pop the root table*/
	}
}

// Extend the different delegates.
%extend MyGUI::delegates::CDelegate4 {
	void set(HSQUIRRELVM vm, HSQOBJECT env, HSQOBJECT func) {
		*$self = new delegate_helper::SquirrelDelegate4<TP1, TP2, TP3, TP4> (vm, env, func); 
	}
	
	void set(HSQUIRRELVM vm, HSQOBJECT func) {
		HSQOBJECT env;
		
		sq_pushroottable(vm);
		sq_resetobject(&env);
		sq_getstackobj(vm, -1, &env);
		*$self = new delegate_helper::SquirrelDelegate4<TP1, TP2, TP3, TP4> (vm, env, func);
		sq_pop(vm, 1); /* pop the root table*/
	}
}